#include <math.h>
#include "demo.h"

struct _Application
{
	GtkWidget *image;
	Enesim_Buffer *output;
	Enesim_Surface *backbuffer;
	Enesim_Renderer *background;
	Enesim_Renderer *renderer;
	GtkWidget *renderer_options;
	/* common options */
};


typedef struct _Demo
{
	char *name;
	Enesim_Renderer  * (*setup)(Application *app, GtkWidget *frame);
} Demo;

enum
{
	LIST_RENDERER = 0,
	COLUMNS,
};

static void destroy (void)
{
	gtk_main_quit ();
}

static Demo demos[] = {
	{ "Checker", enesim_renderer_checker_setup },
	{ "Stripes", enesim_renderer_stripes_setup },
	{ "Grid", enesim_renderer_grid_setup },
	{ "Perlin Noise", enesim_renderer_perlin_setup },
	{ "Circle", enesim_renderer_circle_setup },
	{ "Ellipse", enesim_renderer_ellipse_setup },
	{ "Rectangle", enesim_renderer_rectangle_setup },
	{ "Fancy Button 01", fancy_button01_setup },
	{ "Fancy Progress 01", fancy_progress01_setup },
#ifdef HAVE_EMAGE
	{ "Displacement Map", enesim_renderer_dispmap_setup },
	{ "Radial Distortion", enesim_renderer_raddist_setup },
#endif
#ifdef HAVE_ETEX
	{ "Etex", enesim_text_setup },
	{ "Fancy Button 02", fancy_button02_setup },
#endif
};

static void on_outline_weight_changed(GtkSpinButton *spin, gpointer user_data)
{
	Application *app = user_data;
	gdouble value;
	value = gtk_spin_button_get_value(spin);
	enesim_renderer_shape_outline_weight_set(app->renderer, value);
	application_render(app);
}

static void on_outline_color_changed(GtkColorButton *widget, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Color color;
	GdkColor gdkcolor;
	guint16 alpha;
	gtk_color_button_get_color(widget, &gdkcolor);
	alpha = gtk_color_button_get_alpha(widget);
	enesim_color_from_gdk(&color, &gdkcolor, alpha);
	enesim_renderer_shape_outline_color_set(app->renderer, color);
	application_render(app);
}

static void on_fill_color_changed(GtkColorButton *widget, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Color color;
	GdkColor gdkcolor;
	guint16 alpha;
	gtk_color_button_get_color(widget, &gdkcolor);
	alpha = gtk_color_button_get_alpha(widget);
	enesim_color_from_gdk(&color, &gdkcolor, alpha);
	enesim_renderer_shape_fill_color_set(app->renderer, color);
	application_render(app);
}

static void on_draw_mode_changed(GtkComboBox *combo, gpointer user_data)
{
	Application *app = user_data;
	gchar *text;

	text = gtk_combo_box_get_active_text(combo);
	if (!strcmp(text, "Fill only"))
	{
		enesim_renderer_shape_draw_mode_set(app->renderer, ENESIM_SHAPE_DRAW_MODE_FILL);
	}
	else if (!strcmp(text, "Stroke only"))
	{
		enesim_renderer_shape_draw_mode_set(app->renderer, ENESIM_SHAPE_DRAW_MODE_STROKE);
	}
	else
	{
		enesim_renderer_shape_draw_mode_set(app->renderer, ENESIM_SHAPE_DRAW_MODE_STROKE_FILL);
	}
	application_render(app);
	g_free(text);
}

void shape_setup(Application *app, GtkWidget *box)
{
	{
		GtkWidget *label, *spin;
		label = gtk_label_new("outline_weight");
		gtk_container_add(GTK_CONTAINER(box), label);
		gtk_widget_show(label);
		spin = gtk_spin_button_new_with_range(1.0, 256.0, 0.5);
		g_signal_connect(spin, "value-changed", G_CALLBACK(on_outline_weight_changed), app);
		gtk_container_add(GTK_CONTAINER(box), spin);
		gtk_widget_show(spin);
	}
	{
		GtkWidget *label, *color;
		label = gtk_label_new("outline_color");
		gtk_container_add(GTK_CONTAINER(box), label);
		gtk_widget_show(label);
		color = gtk_color_button_new();
		gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(color), TRUE);
		g_signal_connect(color, "color-set", G_CALLBACK(on_outline_color_changed), app);
		gtk_container_add(GTK_CONTAINER(box), color);
		gtk_widget_show(color);
	}
	{
		GtkWidget *label, *color;
		label = gtk_label_new("fill_color");
		gtk_container_add(GTK_CONTAINER(box), label);
		gtk_widget_show(label);
		color = gtk_color_button_new();
		gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(color), TRUE);
		g_signal_connect(color, "color-set", G_CALLBACK(on_fill_color_changed), app);
		gtk_container_add(GTK_CONTAINER(box), color);
		gtk_widget_show(color);
	}
	{
		GtkComboBox *combo;

		combo = gtk_combo_box_new_text();
		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Fill only");
		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Stroke only");
		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), "Fill and Stroke");
		g_signal_connect(combo, "changed", G_CALLBACK(on_draw_mode_changed), app);
		gtk_container_add(GTK_CONTAINER(box), combo);
		gtk_widget_show(GTK_WIDGET(combo));
	}
}

static void application_display(Application *app)
{
	Eina_Rectangle clip;
	int w, h;

	enesim_surface_size_get(app->backbuffer, &w, &h);
	eina_rectangle_coords_from(&clip, 0, 0, w, h);
	enesim_converter_surface(app->backbuffer, app->output, ENESIM_ANGLE_0, &clip, 0, 0);
	gtk_widget_queue_draw(app->image);
}

static void application_clear(Application *app)
{
	enesim_renderer_draw(app->background, app->backbuffer, ENESIM_FILL, NULL, 0, 0);
}

void application_render(Application *app)
{
	Eina_Rectangle dest;

	application_clear(app);
	eina_rectangle_coords_from(&dest, 128, 128, 256, 256);
	/* TODO also render the bounding box in case it is needed */
	/*
	 * enesim_renderer_boundings(app->renderer, &dest);
	 * r = enesim_renderer_rectangle_new();
	 * enesim_renderer_shape_draw_mode_set(r, ENESIM_DRAW_MODE_STROKE_ONLY);
	 * if (dest.width == 0)
	 * 	dest.width = 256;
	 * if (dest.height == 0)
	 * 	dest.height = 256;
	 * enesim_renderer_draw(r, app->backbuffer, ENESIM_BLEND, &dest, 128, 128);
	 */
	enesim_renderer_draw(app->renderer, app->backbuffer, ENESIM_BLEND, &dest, 128, 128, NULL);
	application_display(app);
}

static void application_demo_select(Application *app, int index)
{
	Demo *d;

	d = &demos[index];
	/* remove previous renderer */
	if (app->renderer)
	{
		GList *l;

		enesim_renderer_unref(app->renderer);
		/* remove every widget it might have created */
		for (l = gtk_container_get_children(GTK_CONTAINER(app->renderer_options)); l; l = l->next)
		{
			gtk_widget_destroy(GTK_WIDGET(l->data));
		}
		g_list_free(l);		
		app->renderer = NULL;
	}
	/* get the frame widget, set the label and send the contents */
	gtk_frame_set_label(GTK_FRAME(app->renderer_options), d->name);
	gtk_widget_show(app->renderer_options);
	if (d->setup)
	{
		app->renderer = d->setup(app, app->renderer_options);
		application_render(app);
	}
}

static void application_setup(Application *app, GtkWidget *image, GtkWidget *options)
{
	Enesim_Buffer_Sw_Data data;
	Enesim_Renderer *background;
	GdkPixbuf *pixbuf;
	int width;
	int height;

	app->renderer = NULL;
	app->image = image;
	app->renderer_options = options;

	pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
	data.rgb888.plane0 = gdk_pixbuf_get_pixels(pixbuf);
	data.rgb888.plane0_stride = gdk_pixbuf_get_rowstride(pixbuf);
	width = gdk_pixbuf_get_width(pixbuf);
	height = gdk_pixbuf_get_height(pixbuf);
	app->output = enesim_buffer_new_data_from(ENESIM_BACKEND_SOFTWARE,
			ENESIM_CONVERTER_RGB888,
			width, height,
			&data);
	app->backbuffer = enesim_surface_new(ENESIM_BACKEND_SOFTWARE,
			ENESIM_FORMAT_ARGB8888,
			width, height);
	background = enesim_renderer_checker_new();
	enesim_renderer_checker_height_set(background, 10);
	enesim_renderer_checker_width_set(background, 10);
	enesim_renderer_checker_even_color_set(background, 0xff555555);
	enesim_renderer_checker_odd_color_set(background, 0xff525252);
	app->background = background;
	application_clear(app);
	application_demo_select(app, 0);
}

static void application_cleanup(Application *app)
{

}

static void on_renderer_changed(GtkWidget *widget, gpointer data)
{
	GtkTreeIter iter;
	GtkTreeModel *model;
	char *value;
	Application *app = data;

	if (gtk_tree_selection_get_selected(GTK_TREE_SELECTION(widget),
			&model, &iter))
	{
		gchar *path;
		int index;

		gtk_tree_model_get(model, &iter, LIST_RENDERER, &value,  -1);
		path = gtk_tree_model_get_string_from_iter(model, &iter);
		index = strtol(path, NULL, 10);
		g_free(path);
		g_free(value);
		application_demo_select(app, index);
	}
}

static void on_affine_changed(GtkButton *button, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Matrix m;
	Enesim_Matrix tmp;

	if (!app->renderer) return;
	enesim_matrix_identity(&m);
	enesim_matrix_translate(&m, 512/2, 512/2);
	enesim_matrix_scale(&tmp, 1.0 / 0.5, 1.0 / 0.5);
	enesim_matrix_compose(&m, &tmp, &m);
	enesim_matrix_rotate(&tmp, M_PI/4);
	enesim_matrix_compose(&m, &tmp, &m);
	enesim_matrix_translate(&tmp, -512/2, -512/2);
	enesim_matrix_compose(&m, &tmp, &m);

	enesim_renderer_transformation_set(app->renderer, &m);
	application_render(app);
}

static void on_projective_changed(GtkButton *button, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Matrix m;
	Enesim_Quad q1, q2;

	if (!app->renderer) return;

	enesim_matrix_identity(&m);
	enesim_quad_coords_set(&q1, 0, 0, 512, 0, 512, 512, 0, 512);
	enesim_quad_coords_set(&q2, 0, 100, 180, 0, 250, 180, 190, 240);
	enesim_matrix_quad_quad_to(&m, &q2, &q1);
	enesim_renderer_transformation_set(app->renderer, &m);
	application_render(app);
}

static void on_identity_changed(GtkButton *button, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Matrix m;


	if (!app->renderer) return;
	enesim_matrix_identity(&m);
	enesim_renderer_transformation_set(app->renderer, &m);
	application_render(app);
}

static void on_x_changed(GtkSpinButton *spin, gpointer user_data)
{
	Application *app = user_data;
	gfloat value;
	gfloat x, y;

	value = gtk_spin_button_get_value_as_float(spin);
	enesim_renderer_origin_get(app->renderer, &x, &y);
	enesim_renderer_origin_set(app->renderer, value, y);
	application_render(app);
}

static void on_y_changed(GtkSpinButton *spin, gpointer user_data)
{
	Application *app = user_data;
	gfloat value;
	gfloat x, y;

	value = gtk_spin_button_get_value_as_float(spin);
	enesim_renderer_origin_get(app->renderer, &x, &y);
	enesim_renderer_origin_set(app->renderer, x, value);
	application_render(app);
}

static void on_color_changed(GtkColorButton *widget, gpointer user_data)
{
	Application *app = user_data;
	Enesim_Color color;
	GdkColor gdkcolor;
	guint16 alpha;

	gtk_color_button_get_color(widget, &gdkcolor);
	alpha = gtk_color_button_get_alpha(widget);
	enesim_color_from_gdk(&color, &gdkcolor, alpha);
	enesim_renderer_color_set(app->renderer, color);
	application_render(app);
}

int main(int argc, char **argv)
{
	GtkWidget *window, *box, *list, *vbox, *image, *event, *frame, *hbox, *button, *spin, *label, *color;
	GtkTreeSelection *selection;
	GtkListStore *model;
	GtkTreeIter iter;
	GtkCellRenderer *renderer;
	GtkTreePath *path;
	int i;
	GdkPixbuf *pixbuf;
	Application app;

	gtk_init (&argc, &argv);
	enesim_init();
#ifdef HAVE_EMAGE
	enesim_image_init();
#endif
#ifdef HAVE_ETEX
	enesim_text_init();
#endif

	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_signal_connect (GTK_OBJECT (window), "destroy",
			GTK_SIGNAL_FUNC (destroy), NULL);

	box = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(window), box);

	/* The left side: renderers */
	list = gtk_tree_view_new();
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (list), -1, "Renderer", renderer, "text", LIST_RENDERER, NULL);
	model = gtk_list_store_new(1, G_TYPE_STRING);
	for (i = 0; i < sizeof(demos) / sizeof(Demo); i++)
	{
		Demo *d = &demos[i];

		gtk_list_store_append(model, &iter);
		gtk_list_store_set(model, &iter, LIST_RENDERER, d->name, -1);
	}

	gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(model));
	gtk_container_add(GTK_CONTAINER(box), list);
	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
	path = gtk_tree_path_new_from_indices(0, -1);
	gtk_tree_selection_select_path(selection, path);
	gtk_tree_path_free(path);
	gtk_widget_show(list);

	/* The right side: image and renderer options */
	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(box), vbox);
	/* The place where the renderers will draw */
	pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 512, 512);
	event = gtk_event_box_new();
	gtk_container_add(GTK_CONTAINER(vbox), event);
	image = gtk_image_new_from_pixbuf(pixbuf);
	gtk_widget_set_size_request(GTK_WIDGET(image), 512, 512);
	gtk_container_add(GTK_CONTAINER(event), image);
	gtk_widget_show(image);
	gtk_widget_show(event);

	/* The options */
	frame = gtk_frame_new("Common Renderer Options");
	gtk_container_add(GTK_CONTAINER(vbox), frame);
	hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(frame), hbox);
	/* Identity */
	button = gtk_button_new_with_label("Identity");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	g_signal_connect(button, "clicked", G_CALLBACK(on_identity_changed), &app);
	gtk_widget_show(button);
	/* Affine */
	button = gtk_button_new_with_label("Affine");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	g_signal_connect(button, "clicked", G_CALLBACK(on_affine_changed), &app);
	gtk_widget_show(button);
	/* Projective */
	button = gtk_button_new_with_label("Projective");
	gtk_container_add(GTK_CONTAINER(hbox), button);
	g_signal_connect(button, "clicked", G_CALLBACK(on_projective_changed), &app);
	gtk_widget_show(button);
	/* X */
	label = gtk_label_new("X");
	gtk_container_add(GTK_CONTAINER(hbox), label);
	gtk_widget_show(label);
	spin = gtk_spin_button_new_with_range(-256, 256, 0.5);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0);
	g_signal_connect(spin, "value-changed", G_CALLBACK(on_x_changed), &app);
	gtk_container_add(GTK_CONTAINER(hbox), spin);
	gtk_widget_show(spin);
	/* Y */
	label = gtk_label_new("Y");
	gtk_container_add(GTK_CONTAINER(hbox), label);
	gtk_widget_show(label);
	spin = gtk_spin_button_new_with_range(-256, 256, 0.5);
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), 0);
	g_signal_connect(spin, "value-changed", G_CALLBACK(on_y_changed), &app);
	gtk_container_add(GTK_CONTAINER(hbox), spin);
	gtk_widget_show(spin);
	/* color */
	label = gtk_label_new("color");
	gtk_container_add(GTK_CONTAINER(hbox), label);
	gtk_widget_show(label);
	color = gtk_color_button_new();
	gtk_color_button_set_use_alpha(GTK_COLOR_BUTTON(color), TRUE);
	g_signal_connect(color, "color-set", G_CALLBACK(on_color_changed), &app);
	gtk_container_add(GTK_CONTAINER(hbox), color);
	gtk_widget_show(color);

	gtk_widget_show(hbox);
	gtk_widget_show(frame);

	frame = gtk_frame_new("Specific Renderer Options");
	gtk_container_add(GTK_CONTAINER(vbox), frame);

	gtk_widget_show(vbox);
	gtk_widget_show(box);
	gtk_widget_show (window);

	/* The callbacks */
	g_signal_connect(selection, "changed", G_CALLBACK(on_renderer_changed), &app);

	/* Setup the application */
	application_setup(&app, image, frame);

	gtk_main ();
#ifdef HAVE_ETEX
	enesim_text_shutdown();
#endif
#ifdef HAVE_EMAGE
	enesim_image_shutdown();
#endif
	enesim_shutdown();

	return 0;
}
